package com.thp.proxy.custom;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * 自定义的Proxy类
 * 
 * 实现的功能:
 * 	  生成代理对象的代码
 * @author Tommy
 *
 */
public class MyProxy {
	
	// 换行
	private static String ln = "\r\n";
	
	
	// 获取代理对象的方法
	public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h) {
		
		
		try {
			
			// 1,生成源代码
			String proxySrc = generateSrc(interfaces[0]);
			
			// 2,将生成的源代码输出到磁盘保存为.java文件
			// 必须生成在   com.thp.proxy.custom  这个包下
			
			String filePath = MyProxy.class.getResource("").getPath();
			File f = new File(filePath + "$Proxy0.java");
			FileWriter fw = new FileWriter(f);
			fw.write(proxySrc);
			fw.flush();
			fw.close();
		
		
		
			// 3,编译源代码, 并且生成.class文件
			JavaCompiler complier = ToolProvider.getSystemJavaCompiler();  // 编译器
			
			System.out.println("complier :" + complier.getClass());
			
			// 编译器工具有一个相关的标准文件管理器，它是工具（或内置）的本机文件管理器。
			StandardJavaFileManager manager = complier.getStandardFileManager(null, null, null);
			
			System.out.println("manager :" + manager.getClass());
			
			Iterable iterable = manager.getJavaFileObjects(f);
			
			System.out.println("itreable :" + iterable);
			
			
			
			CompilationTask task = complier.getTask(null, manager, null, null, null, iterable);
			System.out.println("task :" + task);
			
			
			task.call();
			manager.close();
			
			
			// 4,将class文件中的内容动态加载到JVM中来
			
			
			
			// 5,返回被代理后的代理对象
			Class proxyClass = classLoader.findClass("$Proxy0");
			// 获取构造方法
			Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
			
			// 删除生成的.java文件
			// f.delete();
			
			// 通过MyClassLoader将$MyProxy加载到JVM中来
			return c.newInstance(h);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	// 生成源代码
	// 千万要注意  不能错一个字,错了的话,就可能编译通不过
	private static String generateSrc(Class<?> interfaces) {
		
		StringBuffer src = new StringBuffer();
		src.append("package com.thp.proxy.custom;" + ln);		
		// 导包
		src.append("import java.lang.reflect.Method;" + ln);
		
		src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
		
		
		// 持有  MyInvocationHandler 的引用
		src.append("MyInvocationHandler h;" + ln);
		
		// 构造方法
		src.append("public $Proxy0(MyInvocationHandler h) {" + ln);
		src.append("this.h = h;" + ln);
		src.append("}" + ln);
		int i=0;
		// 将方法循环出来
		for(Method m : interfaces.getMethods()) {
			src.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
			
			src.append("try {" + ln);
			
			src.append("Method m"+i+" = " + interfaces.getName() +".class.getMethod(\""+ m.getName() +"\",new Class[]{});" + ln);
			// 调用方法  (简化操作没有参数)
			src.append("this.h.invoke(this,m"+i+",null);" + ln);
			
			src.append("} catch(Throwable e) { e.printStackTrace(); }" + ln);
			// 返回值处理   我这里简化了操作,没有返回值
			/*if(!m.getReturnType().getName().equals("void")) {
				src.append("return ");
			}*/
			
			src.append("}" + ln);
			i++;
		}
		
		
		
		
		src.append("}");
		
		
		
		
		return src.toString();
	}
	
	
	
	
	
}
